﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace AMS.Chess.CommonLayer
{
    /// <remarks>
    /// This class depends heavily on DateTime.Now, which calls the system clock.  Possible vulnerability if someone messes with the system clock.
    /// </remarks>
    public sealed class PlayClock
    {
        private static PlayClock clock;
        private static Object lockObject = new Object();

        private ChessColor _activePlayer;
        private int _whiteTurns;
        private int _blackTurns;

        private Timer _timer;
        private DateTime _lastTick;

        public TimeSpan TimeLimitPerPlayer { get; set; }
        public int MovesThresholdPerPlayer { get; set; }  // if 0 or less, no moves threshold
        public TimeSpan WhiteTotalTime { get; private set; }    // as object, anyone can modify this; hack vulnerability?
        public TimeSpan BlackTotalTime { get; private set; }

        public static PlayClock GetSingletonInstance()
        {
            if (clock == null)
            {
                lock (lockObject)
                {
                    if (clock == null)
                    {
                        clock = new PlayClock();
                    }
                }
            }

            return clock;
        }

        private PlayClock()
        {
            this._observers = new List<TimeExpiredDelegate>();
            this._timer = new Timer();
            _timer.Interval = 100; // clock is accurate to 100 milliseconds
            _timer.Tick += new EventHandler(HandleTickEvent);
        }

        #region Observer Pattern code
        private List<TimeExpiredDelegate> _observers;

        public delegate void TimeExpiredDelegate(ChessColor activePlayer);

        public void SubscribeTimeExpired(TimeExpiredDelegate delegateMethod)
        {
            // one subscription per person
            if (_observers.Contains(delegateMethod))
                return;

            Console.WriteLine("PlayClock: Someone subscribed to my Expiration notifier.");

            _observers.Add(delegateMethod);
        }

        public void UnsubscribeTimeExpired(TimeExpiredDelegate delegateMethod)
        {
            if (_observers.Contains(delegateMethod))
                _observers.Remove(delegateMethod);
        }

        public void NotifyExpiredTime()
        {
            Console.WriteLine("PlayClock: I am sending out notification of expiration.");
            // notify all observers that time has expired
            foreach (TimeExpiredDelegate teDelegate in _observers)
                teDelegate(_activePlayer);
        }
        #endregion

        private void HandleTickEvent(object sender, EventArgs e)
        {
            DateTime nowTick = DateTime.Now;

            if (_lastTick == null)
                return;
            
            //increment active player's total time
            TimeSpan timeSinceLastTick = _lastTick - nowTick;
            if (_activePlayer == ChessColor.White)
            {
                WhiteTotalTime += timeSinceLastTick;
                if (_whiteTurns >= MovesThresholdPerPlayer) {
                    return;
                }
                else if (WhiteTotalTime >= TimeLimitPerPlayer) {
                    NotifyExpiredTime();
                }
            }
            else
            {
                BlackTotalTime += timeSinceLastTick;
                if (_blackTurns >= MovesThresholdPerPlayer)
                {
                    return;
                }
                else if (WhiteTotalTime >= TimeLimitPerPlayer)
                {
                    NotifyExpiredTime();
                }
            }
        }

        public void StartClock()
        {
            _lastTick = DateTime.Now;
            _timer.Enabled = true;
            _timer.Start();
        }

        public void SwitchActivePlayer()
        {
            // add time up to now to current player's time
            DateTime _hitTime = DateTime.Now;

            // calculate time that previous player just added to their clock
            TimeSpan span = _hitTime - _lastTick;
            if (_activePlayer == ChessColor.White)
            {
                WhiteTotalTime += span;
                _activePlayer = ChessColor.Black;
            }
            else
            {
                BlackTotalTime += span;
                _activePlayer = ChessColor.White;
            }

            // start accounting new player's time
            _lastTick = _hitTime;
        }

        public void ResetClock()
        {
            _timer.Stop();
            _timer.Enabled = false;
            _activePlayer = ChessColor.White;
            WhiteTotalTime = TimeSpan.MinValue;
            BlackTotalTime = TimeSpan.MinValue;
        }

        public void StopClock()
        {
            _timer.Stop();
            _timer.Enabled = false;
        }
    }
}
